#include "parse_mini_vcf.h"

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/io.hpp>
#include <boost/spirit/home/phoenix/object/construct.hpp>
#include <boost/tr1/tuple.hpp>
#include <boost/spirit/home/phoenix/core/argument.hpp>
#include <boost/spirit/home/phoenix/algorithm.hpp>
#include <boost/spirit/repository/include/qi_flush_multi_pass.hpp>

#include "qi_parse_error_handler.h"

namespace qi = boost::spirit::qi;
namespace karma = boost::spirit::karma;
namespace ascii = boost::spirit::ascii;
namespace phoenix = boost::phoenix;

using std::string;
using std::cerr;
using std::vector;



BOOST_FUSION_ADAPT_STRUCT(
t_strain_data,
(char, genotype1)
(char, genotype2)
(int,  mapping_quality)
(int,  high_quality_genotype)
(int,  genotype_quality)
(int,  depth)
)


BOOST_FUSION_ADAPT_STRUCT(
t_mini_vcf,
(std::string, chromosome)
(unsigned, pos)
(std::string, ref)
(std::string, alt)
(std::vector<t_strain_data>, strain_data)
)


phoenix::function<errorHandlerT> const errorHandler_body = errorHandlerT("VCF Body");


template <typename Iterator>
struct mini_vcf_grammar
: qi::grammar<Iterator, t_mini_vcf()>
{
    mini_vcf_grammar(unsigned count_strains, unsigned& curr_line_number_)
    : mini_vcf_grammar::base_type(vcf), curr_line_number(curr_line_number_)
    {
        using qi::lit;
        using qi::lexeme;
        using ascii::char_;
        using qi::int_;
        using ascii::string;
        using namespace qi::labels;
        using qi::eoi;
        using qi::eol;

        genotype1             %= char_("01234.");  
        genotype2             %= char_("01234.");  
        mapping_quality       %= int_;  
        high_quality_genotype %= int_;  
        genotype_quality      %= int_;  
        depth                 %= int_;  

        strain_data %=          genotype1 > lit("/") > genotype2 
                                > lit(":") >> mapping_quality                 
                                > lit(":") >> high_quality_genotype           
                                > lit(":") >> genotype_quality                
                                > lit(":") >> depth;                           


        tab_delim_str %= +(char_ - '\t');
        chromosome    %= tab_delim_str;
        position      %= int_;  
        tab           =  lit("\t");
        identifier    =  lit(".");
        ignored_str   =  +(char_ - '\t');
        ref           %= tab_delim_str; 
        alt           %= (char_ - '\t') % ',';
        quality       =  ignored_str; 
        filter        =  ignored_str; 
        info          =  ignored_str; 
        format        =  ignored_str;
        vcf           %=    -lit("chr") > chromosome    > tab  
                            > position                  > tab  
                            > identifier                > tab  
                            > ref                       > tab  
                            > alt                       > tab  
                            > quality                   > tab  
                            > filter                    > tab  
                            > info                      > tab  
                            > format
                            > qi::repeat(count_strains)[tab> strain_data]
                            > -eol
			    >> boost::spirit::repository::flush_multi_pass;

        // give names for debug
        tab                  .name("TAB");
        header               .name("Comment/Header line");
        chromosome           .name("Chromosome");
        position             .name("Contig position");
        identifier           .name("Identifier (should be '.')");
        ignored_str          .name("Ignored field");
        ref                  .name("Reference sequence");
        alt                  .name("Alternative sequence");
        quality              .name("Quality");
        filter               .name("Filter");
        info                 .name("Info");
        format               .name("Format");
        vcf                  .name("VCF");
        strain_data          .name("Strain data"); 
        genotype1            .name("Genotype 1"); 
        genotype2            .name("Genotype 2"); 
        mapping_quality      .name("Mapping quality"); 
        high_quality_genotype.name("High quality genotyp"); 
        genotype_quality     .name("Genotype quality"); 
        depth                .name("Depth"); 

        qi::on_error<qi::fail>                               
        (vcf, ::errorHandler_body(_1, _2, _3, _4, phoenix::ref(curr_line_number)));
    }
    qi::rule<Iterator, void()>            header ;
    qi::rule<Iterator, std::string()>     tab_delim_str ;
    qi::rule<Iterator, std::string()>     chromosome    ;
    qi::rule<Iterator, int()>             position      ;
    qi::rule<Iterator, void()>            tab           ;
    qi::rule<Iterator, void()>            identifier    ;
    qi::rule<Iterator, void()>            ignored_str   ;
    qi::rule<Iterator, std::string()>     ref           ;
    qi::rule<Iterator, std::string()>     alt           ;
    qi::rule<Iterator, void()>            quality       ;
    qi::rule<Iterator, void()>            filter        ;
    qi::rule<Iterator, void()>            info          ;
    qi::rule<Iterator, void()>            format        ;
    qi::rule<Iterator, t_mini_vcf()>      vcf           ;

    qi::rule<Iterator, t_strain_data()>   strain_data ; 
    qi::rule<Iterator, char()>            genotype1            ; 
    qi::rule<Iterator, char()>            genotype2            ; 
    qi::rule<Iterator, int ()>            mapping_quality      ; 
    qi::rule<Iterator, int ()>            high_quality_genotype; 
    qi::rule<Iterator, int ()>            genotype_quality     ; 
    qi::rule<Iterator, int ()>            depth                ; 
	unsigned&							  curr_line_number;
};


sp_vcf_grammar
create_mini_vcf_grammar(unsigned cnt_strains, unsigned& curr_line_number)
{
	return sp_vcf_grammar(new mini_vcf_grammar<string::const_iterator>(cnt_strains, curr_line_number));
}

sp_vcf_vecchar_grammar create_mini_vcf_vecchar_grammar(unsigned cnt_strains, unsigned& curr_line_number)
{
	return sp_vcf_vecchar_grammar(new mini_vcf_grammar<t_storage::const_iterator>(cnt_strains, curr_line_number));
}


bool parse_mini_vcf(sp_vcf_grammar p_vcf_grammar, string::const_iterator& beg, string::const_iterator end, t_mini_vcf& res)
{
	mini_vcf_grammar<string::const_iterator>& vcf_grammar = *p_vcf_grammar;
    return qi::parse(beg, end, vcf_grammar, res);
}
    

bool parse_mini_vcf(sp_vcf_vecchar_grammar p_vcf_grammar, t_storage::const_iterator& beg, t_storage::const_iterator end, t_mini_vcf& res)
{
	mini_vcf_grammar<t_storage::const_iterator>& vcf_grammar = *p_vcf_grammar;
    return qi::parse(beg, end, vcf_grammar, res);
}
    
